using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Xml;
using System.Diagnostics;

namespace AutoUpdate
{
    /// <summary>
    /// This class manage the autoupdate of ViewOn.
    /// 
    /// - force download
    /// </summary>
    sealed public class AutoUpdateManager
    {
        #region Variables

        static private string SoftVersion = "ViewOn";

        //---- Current Version
        static private Hashtable _currentModules = new Hashtable();

        //---- New version
        static private ArrayList _modules = new ArrayList();

        //---- Servers
        static private ArrayList _updateServers = new ArrayList();

        //----
        static private bool _useProxy = false;
        static private string _proxyAddress = null;
        static private int _proxyPort = -1;

        //---- Downloads list
        static private ArrayList _downloadJobs = new ArrayList();

        //----
        static private XmlDocument _xmlDocument;

        #endregion

        #region Constructor

        static AutoUpdateManager()
        {
            // Add the default server
            _updateServers.Add("http://www.viewon.tv/software/autoupdate/");
        }

        #endregion

        #region Properties

        static public ArrayList Modules
        {
            get
            {
                return _modules;
            }
        }

        static public bool UseProxy
        {
            set
            {
                _useProxy = true;
            }
            get
            {
                return _useProxy;
            }
        }

        static public string ProxyAddress
        {
            set
            {
                _proxyAddress = value;
            }
        }

        static public int ProxyPort
        {
            set
            {
                _proxyPort = value;
            }
        }

        #endregion

        #region Check Update

        static public AutoUpdateResult CheckAutoUpdate()
        {
            _modules.Clear();
            _currentModules.Clear();

            //---- Load current versions
            try
            {
                LoadCurrentInstallInfo();
            }
            catch
            {
                AutoUpdateResult result = new AutoUpdateResult();
                result.IsOk = false;
                result.Message = "Unable to load auto update information";
                return result;
            }

            //---- Download the ufoz.au.xml file
            string errorMessage = DownloadFile(SoftVersion + ".au.xml", "autoupdate/");
            if (errorMessage.Length != 0)
            {
                AutoUpdateResult result = new AutoUpdateResult();
                result.IsOk = false;
                result.NewVersionExists = false;
                result.Message = errorMessage;
                // Delete folder
                try
                {
                    Directory.Delete(AppDomain.CurrentDomain.BaseDirectory + "autoupdate/", true);
                }
                catch (Exception exception) { }
                return result;
            }

            //---- Read the ufoz.au.xml file and check the version
            AutoUpdateResult result2 = new AutoUpdateResult();
            result2.Version = LoadDotAUFile();

            // Delete folder
            try
            {
                Directory.Delete(AppDomain.CurrentDomain.BaseDirectory + "autoupdate/", true);
            }
            catch (Exception exception) { }

            //---- Check if there are new version
            bool update = false;
            bool forceDownload = false;
            ArrayList modulesToRemove = new ArrayList();
            foreach (Module module in _modules)
            {
                ExistingModuleInfo moduleInfo = (ExistingModuleInfo)_currentModules[module.Name];

                if (moduleInfo == null) update = true;
                else if (module.MajorVersion > moduleInfo.MajorVersion) update = true;
                else if (module.MajorVersion == moduleInfo.MajorVersion &&
                        module.MinorVersion > moduleInfo.MinorVersion)
                        update = true;
                else modulesToRemove.Add(module);

                if (module.ForceDownload)
                    forceDownload = true;
            }

            foreach (Module module in modulesToRemove)
                _modules.Remove(module);

            result2.NewVersionExists = update;
            result2.IsOk = true;
            result2.ForceDownload = forceDownload;
            return result2;
        }

        #endregion

        #region DownloadUpdate

        /// <summary>
        /// Download the files for the update.
        /// </summary>
        static public void DownloadUpdate()
        {
            foreach (Module module in _modules)
                foreach (ModuleFile file in module.Files)
                    AddDownloadJob(file.Path, "autoupdate/");

            // Download files
            foreach (DownloadJob job in _downloadJobs)
                DownloadFile(job.FileURL, job.TargetFolder);
        }

        #endregion

        #region Update

        /// <summary>
        /// Kill all the processes and restart
        /// </summary>
        static public void Update()
        {
            // Stop ViewOn
            Process[] processes = Process.GetProcessesByName("ViewOn");
            foreach (Process process in processes)
                process.Kill();

            // Replace files
            ReplaceFiles();

            // Register COM files
            RegisterCOM();

            // Delete folder
            try
            {
                Directory.Delete(AppDomain.CurrentDomain.BaseDirectory + "autoupdate/", true);
            }
            catch (Exception exception) { }

            // Restart
            try
            {
                Process.Start("ViewOn.exe");
            }
            catch (Exception e)
            {
            }
        }

        static private void ReplaceFiles()
        {
            foreach (Module module in _modules)
                foreach (ModuleFile file in module.Files)
                    try
                    {
                        File.Move(AppDomain.CurrentDomain.BaseDirectory + "autoupdate/" + file.Path, AppDomain.CurrentDomain.BaseDirectory + file.Path);
                    }
                    catch (Exception exception)
                    {
                    }
        }

        static private void RegisterCOM()
        {
            foreach (Module module in _modules)
                foreach (ModuleFile file in module.Files)
                    try
                    {
                        DllRegServer regServer = new DllRegServer(file.Path);
                        regServer.Register();
                    }
                    catch (Exception exception)
                    {
                    }
        }

        #endregion

        #region viewon.au.xml

        static private void LoadCurrentInstallInfo()
        {
            string file = AppDomain.CurrentDomain.BaseDirectory + "ViewOn.exe.config";

            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(file);

            XmlNodeList moduleNodes = xmlDocument.SelectNodes("//configuration/ViewOn/viewOnModules/viewOnModule");
            foreach (XmlNode moduleNode in moduleNodes)
            {
                ExistingModuleInfo moduleInfo = new ExistingModuleInfo();
                moduleInfo.MajorVersion = Int32.Parse(moduleNode.Attributes["majorVersion"].Value);
                moduleInfo.MinorVersion = Int32.Parse(moduleNode.Attributes["minorVersion"].Value);
                moduleInfo.Name = moduleNode.Attributes["name"].Value;

                _currentModules.Add(moduleInfo.Name, moduleInfo);
            }
        }

        static private string LoadDotAUFile()
        {
            string version = "";
            string file = AppDomain.CurrentDomain.BaseDirectory + "autoupdate\\" + SoftVersion + ".au.xml";

            _xmlDocument = new XmlDocument();
            _xmlDocument.Load(file);

            //---- Update
            XmlNode updateNode = _xmlDocument.SelectSingleNode("//autoupdate/update");

            XmlNodeList moduleNodes = _xmlDocument.SelectNodes("//autoupdate/update/module");
            foreach (XmlNode moduleNode in moduleNodes)
            {
                // Module load
                Module newModule = new Module();
                newModule.MajorVersion = Int32.Parse(moduleNode.Attributes["majorVersion"].Value);
                newModule.MinorVersion = Int32.Parse(moduleNode.Attributes["minorVersion"].Value);
                newModule.Name = moduleNode.Attributes["name"].Value;
                try
                {
                    newModule.ForceDownload = Boolean.Parse(moduleNode.Attributes["ForceDownload"].Value);
                }
                catch { }
                XmlNode descriptionNode = moduleNode.SelectSingleNode("description");
                newModule.Description = descriptionNode.InnerText;

                _modules.Add(newModule);

                // Set version
                if (newModule.Name == "ViewOn")
                    version = newModule.MajorVersion + "." + newModule.MinorVersion;

                // Load files
                foreach (XmlNode fileNode in moduleNode.SelectNodes("file"))
                {
                    ModuleFile moduleFile = new ModuleFile(fileNode.Attributes["name"].Value);
                    try
                    {
                        moduleFile.IsCOMToRegister = bool.Parse(fileNode.Attributes["IsCOMToRegister"].Value);
                    }
                    catch { }
                    newModule.Files.Add(moduleFile);
                }
            }

            return version;
        }

        #endregion

        #region Add Download Job

        static private void AddDownloadJob(string fileURL, string targetFolder)
        {
            DownloadJob job = new DownloadJob();
            job.FileURL = fileURL;
            job.TargetFolder = targetFolder;

            _downloadJobs.Add(job);
        }

        #endregion

        #region Download File

        static private string DownloadFile(string fileURL, string targetFolder)
        {
            string fullFileURL = ((string)_updateServers[0]) + fileURL;

            try
            {
                //---- Get the Default proxy settings (IE);
                IWebProxy proxy = WebProxy.GetDefaultProxy();
                System.Net.GlobalProxySelection.Select = proxy;
                System.Net.GlobalProxySelection.Select.Credentials = CredentialCache.DefaultCredentials;

                if (_useProxy)
                {
                    //proxy.Credentials = new System.Net.NetworkCredential("user", "password", "domain");
                    if (proxy is WebProxy)
                    {
                        ((WebProxy)proxy).Address = new Uri("http://" + _proxyAddress + ":" + _proxyPort);
                        ((WebProxy)proxy).BypassProxyOnLocal = true;
                    }
                }

                WebRequest request = WebRequest.Create(fullFileURL);
                request.Proxy = proxy;

                //---- Retreive the stream
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                System.IO.Stream stream = response.GetResponseStream();

                //---- Create the file
                Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + "autoupdate\\");
                FileStream fileStream = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "autoupdate\\" + fileURL, FileMode.OpenOrCreate, FileAccess.Write);

                //---- Download
                byte[] buffer = new byte[256];
                int len = stream.Read(buffer, 0, 256);
                while (len > 0)
                {
                    fileStream.Write(buffer, 0, len);

                    // Next chunk
                    len = stream.Read(buffer, 0, 256);
                }

                response.Close();
                stream.Close();
                fileStream.Close();
            }
            catch (Exception exception)
            {
                return "Cannot connect to Update Server, please check your proxy settings";
            }

            return "";
        }


        #endregion

    }

    #region DownloadJob, Module, ModuleInfo, AutoUpdateResult

    class DownloadJob
    {
        public string FileURL;
        public string TargetFolder;
    }

    class Module
    {
        public string Name;
        public bool ForceDownload;
        public int MajorVersion;
        public int MinorVersion;
        public string Description;
        public List<ModuleFile> Files = new List<ModuleFile>();
    }

    class ModuleFile
    {
        public string Path;
        public bool IsCOMToRegister = false;

        public ModuleFile(string path)
        {
            Path = path;
        }
    }

    class ExistingModuleInfo
    {
        public string Name;
        public int MajorVersion;
        public int MinorVersion;
    }

    public class AutoUpdateResult
    {
        public bool IsOk;
        public bool NewVersionExists;
        public string Message;
        public bool ForceDownload;
        public string Version;
    }

    #endregion
}
